{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center> 理解 Python 异步网络编程 </center>\n",
    "# <center> - 视频后端 Wangningning </center>\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<center><img align=\"center\" src=\"imgs/python-async-programming.png\"></center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center> 小问题？ </center>\n",
    "\n",
    "-  什么是异步网络编程？为什么它能提高 IO 性能\n",
    "-  Python的异步编程是如何工作的？\n",
    "-  为什么使用协程就能用同步的方式编写异步代码"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center>  从一个 tcp socket 回显服务器说起 </center>\n",
    "\n",
    "- 从一个简单的 TCP client server 引入\n",
    "- 实现一个简单的回显服务器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# Echo client program, code_demo/python_async/tcp_echo_client.py\n",
    "import socket\n",
    "\n",
    "HOST = '127.0.0.1'    # The remote host\n",
    "PORT = 8888              # The same port as used by the server\n",
    "with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:\n",
    "    s.connect((HOST, PORT))\n",
    "    s.sendall(b'Hello, world')\n",
    "    data = s.recv(1024)\n",
    "print('Received', repr(data))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# Echo server program, code_demo/python_async/tcp_echo_server.py\n",
    "import socket\n",
    "\n",
    "HOST = 'localhost'    # The remote host\n",
    "PORT = 8888 # Arbitrary non-privileged port\n",
    "with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:\n",
    "    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n",
    "    s.bind((HOST, PORT))\n",
    "    s.listen(50)\n",
    "    while True:\n",
    "        conn, addr = s.accept()\n",
    "        print('Connected by', addr)\n",
    "        with conn:\n",
    "            while 1:\n",
    "                data = conn.recv(1024)\n",
    "                if not data:\n",
    "                    break\n",
    "                conn.sendall(data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "# <center>  同步 socket 服务器的缺点 </center>\n",
    "\n",
    "- 阻塞的\n",
    " - socket系统调用 accept/recv/sendall 等需要等待返回\n",
    "- 同步的\n",
    " - client 发送请求后需要等待内核 IO 操作完成后才能继续执行，后续客户端需要等待"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center>  引入 Linux IO 多路复用机制 </center>\n",
    "\n",
    "- Linux select/poll/epoll\n",
    "- python2 select 模块\n",
    "- python3 selectors 模块"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "# <center>  Python3 selectors 模块 </center>\n",
    "\n",
    "- 封装了操作系统的 IO 复用机制\n",
    "- 替代 python2 偏底层的 select 模块\n",
    "- 提供了更好的抽象和更简洁的 API\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "# <center> selectors 模块 </center>\n",
    "\n",
    "- 事件类型：EVENT_READ, EVENT_WRITE\n",
    "- DefaultSelector: 自动根据平台选取合适的 IO 模型\n",
    "    - register(fileobj, events, data=None)\n",
    "    - unregister(fileobj)\n",
    "    - modify(fileobj, events, data=None)\n",
    "    - select(timeout=None): returns[(key, events)]\n",
    "    - close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# selectors 简单使用方式, code_demo/python_async/tcp_echo_server_callback.py\n",
    "\"\"\"\n",
    "python3 selectos 模块演示，一个简单的异步 tcp 回显服务器\n",
    "\"\"\"\n",
    "import selectors\n",
    "import socket\n",
    "\n",
    "sel = selectors.DefaultSelector()  # 定义一个 selector\n",
    "\n",
    "\n",
    "def accept(sock, mask):\n",
    "    conn, addr = sock.accept()  # Should be ready\n",
    "    print('accepted', conn, 'from', addr)\n",
    "    conn.setblocking(False) # 注意这里的 setblocking 为 False\n",
    "    sel.register(conn, selectors.EVENT_READ, read)   # 注册监听可读事件的回调\n",
    "\n",
    "\n",
    "def read(conn, mask):\n",
    "    data = conn.recv(1000)  # Should be ready\n",
    "    if data:\n",
    "        print('echoing', repr(data), 'to', conn)\n",
    "        conn.send(data)  # Hope it won't block\n",
    "    else:\n",
    "        print('closing', conn)\n",
    "        sel.unregister(conn)  # 取消对 conn socket 的事件监听\n",
    "        conn.close()\n",
    "\n",
    "\n",
    "sock = socket.socket()\n",
    "sock.bind(('localhost', 1234))\n",
    "sock.listen(100)\n",
    "sock.setblocking(False)\n",
    "sel.register(sock, selectors.EVENT_READ, accept)    # sock 可读的时候执行 accept 回调\n",
    "\n",
    "while True:\n",
    "    events = sel.select()   # 等待直到监听的socket 有注册的事件发生\n",
    "    for key, mask in events:\n",
    "        callback = key.data\n",
    "        callback(key.fileobj, mask)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center>  什么是事件循环 EventLoop </center>\n",
    "\n",
    "- 异步编程中经常提到事件循环(EventLoop)的概念\n",
    "- 其实刚才代码中的 while True 里就是事件循环\n",
    "- 在一个死循环里等待 selector.select() 方法就绪\n",
    "- 然后执行对应 socket 上注册的回调函数\n",
    "- 抽象出 EventLoop 类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "import selectors\n",
    "\n",
    "class EventLoop:\n",
    "\n",
    "    def __init__(self, selector=None):\n",
    "        if selector is None:\n",
    "            selector = selectors.DefaultSelector()\n",
    "        self.selector = selector\n",
    "\n",
    "    def run_forever(self):\n",
    "        while True:\n",
    "            events = self.selector.select()\n",
    "            for key, mask in events:\n",
    "                if mask == selectors.EVENT_READ:\n",
    "                    callback = key.data\n",
    "                    callback(key.fileobj)\n",
    "                else:\n",
    "                    callback, msg = key.data\n",
    "                    callback(key.fileobj, msg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center> 改写 tcp echo server </center>\n",
    "\n",
    "- 使用 selectors 模块改写 tcp 回显服务器\n",
    "- 将 while True 循环改成 EventLoop 类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# 基于回调方式的 tcp 回显 server\n",
    "class TCPEchoServer:\n",
    "    def __init__(self, host, port, loop):\n",
    "        self.host = host\n",
    "        self.port = port\n",
    "        self._loop = loop\n",
    "        self.s = socket.socket()\n",
    "\n",
    "    def run(self):\n",
    "        self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n",
    "        self.s.bind((self.host, self.port))\n",
    "        self.s.listen(128)\n",
    "        self.s.setblocking(False)\n",
    "        self._loop.selector.register(self.s, selectors.EVENT_READ, self._accept)\n",
    "        self._loop.run_forever()\n",
    "\n",
    "    def _accept(self, sock):\n",
    "        conn, addr = sock.accept()\n",
    "        print('accepted', conn, 'from', addr)\n",
    "        conn.setblocking(False)\n",
    "        self._loop.selector.register(conn, selectors.EVENT_READ, self._on_read)\n",
    "\n",
    "    def _on_read(self, conn):\n",
    "        msg = conn.recv(1024)\n",
    "        if msg:\n",
    "            print('echoing', repr(msg), 'to', conn)\n",
    "            self._loop.selector.modify(conn, selectors.EVENT_WRITE, (self._on_write, msg))\n",
    "        else:\n",
    "            print('closing', conn)\n",
    "            self._loop.selector.unregister(conn)\n",
    "            conn.close()\n",
    "\n",
    "    def _on_write(self, conn, msg):\n",
    "        conn.sendall(msg)\n",
    "        self._loop.selector.modify(conn, selectors.EVENT_READ, self._on_read)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# 启动这个使用回调方式的异步 tcp 回显服务器, code_demo/python_async/tcp_echo_server_callback_eventloop.py\n",
    "event_loop = EventLoop()\n",
    "echo_server = TCPEchoServer('localhost', 8888, event_loop)\n",
    "echo_server.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center> 回调的问题 </center>\n",
    "\n",
    "- 代码逻辑割裂，不容易理解\n",
    "- 多层嵌套，callback hell"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center>  回调的解决方式-使用协程 </center>\n",
    "\n",
    "- 从 Python 生成器引入协程\n",
    "- 什么是基于生成器的协程\n",
    "- 如何使用异步编程中的 Future/Task\n",
    "- 什么是原生协程"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center>  从生成器说起 </center>\n",
    "\n",
    "- Python中生成器是用来生成值的函数 (包含 yield 的函数)\n",
    "- 通常函数使用return返回值然后作用域被销毁，再次调用函数会重新执行\n",
    "- 生成器可以yield一个值之后暂停函数执行，然后控制权交给调用者，之后我们可以恢复其执行并且获取下一个值\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# 生成器 generator 演示, generator_demo.py\n",
    "def simple_gen():\n",
    "    yield 'hello'\n",
    "    yield 'world'\n",
    "\n",
    "gen = simple_gen()\n",
    "print(type(gen))    # 'generator' object\n",
    "print(next(gen))    # 'hello'\n",
    "print(next(gen))    # 'world'\n",
    "print(next(gen))    # 协程结束抛出 StopIteration 异常"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# 注意生成器函数调用的时候不会直接返回值，而是返回一个类似于可迭代对象(iterable)的生成器对象(generator object)，我们可以对生成器对象调用next()函数来迭代值，或者使用for循环。\n",
    "# 生成器常用来节省内存，比如我们可以使用生成器函数yield值来替代返回一个耗费内存的大序列:\n",
    "def f(n):\n",
    "    res = []\n",
    "    for i in range(n):\n",
    "        res.append(i)\n",
    "    return res\n",
    "\n",
    "def yield_n(n):\n",
    "    for i in range(n):\n",
    "        yield i\n",
    "\n",
    "assert f(10) == list(yield_n(10))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center> 什么是基于生成器的协程 </center>\n",
    "\n",
    "- pep 342(Coroutines via Enhanced Generators)对生成器做了增强\n",
    "- yield 关键字既可以用来获取(pull)数据，作为表达式在等号右边也可以发送数据（使用 send 方法）\n",
    "- 还可以通过throw()向生成器内抛出异常以便随时终止生成器的运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# 我们先看一个简单的例子, generator_based_coroutine.py\n",
    "def coro():\n",
    "    hello = yield 'hello'    # yield关键字在=右边作为表达式，可以被send值\n",
    "    yield hello\n",
    "\n",
    "\n",
    "c = coro()\n",
    "print(next(c))    # 输出 'hello'，这里调用 next 产出第一个值 'hello'，之后函数暂停\n",
    "print(c.send('world'))    # 再次调用 send 发送值，此时 hello 变量赋值为 'world', 然后 yield 产出 hello 变量的值 'world'\n",
    "# 之后协程结束，后续再 send 值会抛异常 StopIteration\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "# <center> 基于生成器的协程关键概念 </center>\n",
    "\n",
    "- 协程需要使用 send(None) 或者 next(coroutine) 来『预激』(prime) 才能启动\n",
    "- 在 yield 处协程会暂停执行\n",
    "- 单独的 yield value 会产出值给调用方\n",
    "- 可以通过 coroutine.send(value) 来给协程发送值，发送的值会赋值给 yield 表达式左边的变量 value = yield \n",
    "- 协程执行完成后(没有遇到下一个 yield 语句)会抛出 StopIteration 异常"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "# <center> 协程示例 </center>\n",
    "- c=yield a+b，需要注意的是并不是把 a+b 的结果赋值 c\n",
    "\n",
    "<center><img align=\"center\" src=\"imgs/coro.png\"></center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "# <center> 预激(prime)协程 </center>\n",
    "```py\n",
    "from functools import wraps\n",
    "def coroutine(func):  # 这样就不用每次都用 send(None)启动了\n",
    "\"\"\"装饰器：向前执行到第一个`yield`表达式，预激`func`\"\"\"\n",
    "    @wraps(func)\n",
    "    def primer(*args,**kwargs):  ➊\n",
    "        gen = func(*args,**kwargs)  ➋\n",
    "        next(gen)  ➌\n",
    "        return gen  ➍\n",
    "    return primer\n",
    "```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center> Python3 yield from </center>\n",
    "\n",
    "- Python3 引入了 yield from ，它的主要作用有两个 \n",
    "   1. 链接子生成器\n",
    "   2. 作为委派生成器用来当调用者和子生成器的通道"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    ">>> def gen():\n",
    "...     for c in 'AB':\n",
    "...         yield c\n",
    "...     for i in range(1, 3):\n",
    "...         yield i\n",
    "...\n",
    ">>> list(gen())\n",
    "['A', 'B', 1, 2]\n",
    "\n",
    ">>> def gen():\n",
    "...     yield from 'AB'   # 用 yield from 帮我们省去了很多 for 模板代码\n",
    "...     yield from range(1, 3)\n",
    "...\n",
    ">>> list(gen())\n",
    "['A', 'B', 1, 2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "invalid syntax (<ipython-input-12-1bf512329117>, line 18)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;36m  File \u001b[0;32m\"<ipython-input-12-1bf512329117>\"\u001b[0;36m, line \u001b[0;32m18\u001b[0m\n\u001b[0;31m    result = yield from coro1()  # 这里 coro2 会暂停并把调用者的 send 发送给 coro1() 协程，coro1() 返回后其return 的值会被赋值给 result\u001b[0m\n\u001b[0m                      ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n"
     ]
    }
   ],
   "source": [
    "# 作为委派生成器\n",
    "# “把迭代器当作生成器使用，相当于把子生成器的定义体内联在 yield from 表达式中。此外，子生成器可以执行 return 语句，返回一个值，而返回的值会成为 yield from 表达式的值。”\n",
    "# code_demo/python_async/yield_from_demo.py\n",
    "def coro1():\n",
    "    \"\"\"定义一个简单的基于生成器的协程作为子生成器\"\"\"\n",
    "    word = yield 'hello'\n",
    "    yield word\n",
    "    return word    # 注意这里协程可以返回值了，返回的值会被塞到 StopIteration value 属性 作为 yield from 表达式的返回值\n",
    "\n",
    "\n",
    "def coro2():\n",
    "    \"\"\"委派生成器，起到了调用方和子生成器通道的作用，请仔细理解下边的描述。\n",
    "    委派生成器会在 yield from 表达式处暂停，调用方可以直接发数据发给子生成器，\n",
    "    子生成器再把产出的值发给调用方。\n",
    "    子生成器返回后， 解释器抛出 StopIteration异常， 并把返回值附加到异常对象上，此时委派生成器恢复\n",
    "    \"\"\"\n",
    "    # 子生成器返回后，解释器抛出 StopIteration 异常，返回值被附加到异常对象上，此时委派生成器恢复\n",
    "    result = yield from coro1()  # 这里 coro2 会暂停并把调用者的 send 发送给 coro1() 协程，coro1() 返回后其return 的值会被赋值给 result\n",
    "    print('coro2 result', result)\n",
    "\n",
    "\n",
    "def main():  # 调用方，用来演示调用方通过委派生成器可以直接发送值给子生成器值。这里main 是调用者，coro2 是委派生成器，coro1 是子生成器\n",
    "    c2 = coro2()  # 委派生成器\n",
    "    print(next(c2))   # 这里虽然调用的是 c2 的send，但是会发送给 coro1, 委派生成器进入 coro1 执行到第一个 yield 'hello' 产出 'hello'\n",
    "    print(c2.send('world')) # 委派生成器发送给 coro1，word 赋值为 'world'，之后产出 'world'\n",
    "    try:\n",
    "        # 继续 send 由于 coro1 已经没有 yield 语句了，直接执行到了 return 并且抛出 StopIteration\n",
    "        # 同时返回的结果作为 yield from 表达式的值赋值给左边的 result，接着 coro2() 里输出 \"coro2 result world\"\n",
    "        c2.send(None)\n",
    "    except StopIteration:\n",
    "        pass\n",
    "\n",
    "main()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# RESULT = yield from EXPR 伪代码演示(为了简化，去掉了异常处理)\n",
    "_i = iter(EXPR)  # ❶ EXPR 可以是任何可迭代的对象，因为获取迭代器 _i（这是子生成器）使用的是 iter() 函数。\n",
    "\n",
    "try:\n",
    "    _y = next(_i)  # ❷ 预激子生成器；结果保存在 _y 中，作为产出的第一个值。\n",
    "\n",
    "except StopIteration as _e:\n",
    "    _r = _e.value  # ❸ 如果抛出 StopIteration 异常，获取异常对象的 value 属性，赋值给 _r——这是最简单情况下的返回值（RESULT）。\n",
    "else:\n",
    "    while 1:  # ❹ 运行这个循环时，委派生成器会阻塞，只作为调用方和子生成器之间的通道。\n",
    "        _s = yield _y  # ❺ 产出子生成器当前产出的元素；等待调用方发送 _s 中保存的值。注意，这个代码清单中只有这一个 yield 表达式。\n",
    "        try:\n",
    "            _y = _i.send(_s)  # ❻ 尝试让子生成器向前执行，转发调用方发送的 _s。\n",
    "        except StopIteration as _e:  # ❼ 如果子生成器抛出 StopIteration 异常，获取 value 属性的值，赋值给 _r，然后退出循环，让委派生成器恢复运行。\n",
    "            _r = _e.value\n",
    "            break\n",
    "\n",
    "RESULT = _r  # ❽ 返回的结果（RESULT）是 _r，即整个 yield from 表达式的值。\n",
    "\n",
    "\"\"\"\n",
    "_i（迭代器）\n",
    "\n",
    "　　子生成器\n",
    "\n",
    "_y（产出的值）\n",
    "\n",
    "　　子生成器产出的值\n",
    "\n",
    "_r（结果）\n",
    "\n",
    "　　最终的结果（即子生成器运行结束后 yield from 表达式的值）\n",
    "\n",
    "_s（发送的值）\n",
    "\n",
    "　　调用方发给委派生成器的值，这个值会转发给子生成器\n",
    "\n",
    "_e（异常）\n",
    "\n",
    "　　异常对象（在这段简化的伪代码中始终是 StopIteration 实例）\n",
    "\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# 使用 yield from 改写之后的 TCPEchoServe 主体如下\n",
    "class TCPEchoServer:\n",
    "    def __init__(self, host, port, loop):\n",
    "        self.host = host\n",
    "        self.port = port\n",
    "        self._loop = loop\n",
    "        self.s = socket.socket()\n",
    "\n",
    "    def run(self):\n",
    "        self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n",
    "        self.s.bind((self.host, self.port))\n",
    "        self.s.listen(128)\n",
    "        self.s.setblocking(False)\n",
    "\n",
    "        while True:\n",
    "            conn, addr = yield from self.accept()\n",
    "            msg = yield from self.read(conn)\n",
    "            if msg:\n",
    "                yield from self.sendall(conn, msg)\n",
    "            else:\n",
    "                conn.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center> 异步编程 Future 对象 </center>\n",
    "\n",
    "- 如果不用回调，如何获取到异步调用的结果呢？\n",
    "- Python异步框架中使用到了 Future 对象\n",
    "- 作用：当异步调用执行完的时候，用来保存它的结果\n",
    "- Future 对象的 result 属性用来保存结果\n",
    "- set_result 用来设置 result 并且运行给 Future 对象添加的回调\n",
    "  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# Future 对象的定义\n",
    "class Future:\n",
    "    def __init__(self):\n",
    "        self.result = None   # 保存结果\n",
    "        self._callbacks = []  # 保存对 Future 的回调函数\n",
    "\n",
    "    def add_done_callback(self, fn):\n",
    "        self._callbacks.append(fn)\n",
    "\n",
    "    def set_result(self, result):\n",
    "        self.result = result\n",
    "        for callback in self._callbacks:\n",
    "            callback(self)\n",
    "\n",
    "    def __iter__(self):\n",
    "        \"\"\" 让 Future 对象支持 yield from\"\"\"\n",
    "        yield self  # 产出自己\n",
    "        return self.result   # yield from 将把 result 值返回作为 yield from 表达式的值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# 先来看个回调的例子, future_demo.py\n",
    "# 这个例子中使用了多个嵌套回调， callback3 依赖 callback2 的结果，callback2 又依赖 callback1 的结果。\n",
    "def callback1(a, b):\n",
    "    c = a + b\n",
    "    c = callback2(c)\n",
    "    return c\n",
    "\n",
    "\n",
    "def callback2(c):\n",
    "    c *= 2\n",
    "    callback3(c)\n",
    "    return c\n",
    "\n",
    "\n",
    "def callback3(c):\n",
    "    print(c)\n",
    "\n",
    "\n",
    "def caller(a, b):\n",
    "    callback1(a, b)\n",
    "\n",
    "\n",
    "caller(1, 2)  # 输出 6"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# 使用 yield from 和 Future 对象改写上面的例子\n",
    "def callback_1(a, b):\n",
    "    f = Future()\n",
    "\n",
    "    def on_callback_1():\n",
    "        f.set_result(a+b)\n",
    "\n",
    "    on_callback_1()\n",
    "    c = yield from f\n",
    "    return c\n",
    "\n",
    "\n",
    "def callback_2(c):\n",
    "    f = Future()\n",
    "\n",
    "    def on_callback_2():\n",
    "        f.set_result(c*2)\n",
    "    on_callback_2()\n",
    "    c = yield from f\n",
    "    return c\n",
    "\n",
    "\n",
    "def callback_3(c):\n",
    "    f = Future()\n",
    "\n",
    "    def on_callback_3():\n",
    "        f.set_result(c)\n",
    "    on_callback_3()\n",
    "    yield from f\n",
    "\n",
    "\n",
    "def caller_use_yield_from(a, b):\n",
    "    c1 = yield from callback_1(a, b)\n",
    "    c2 = yield from callback_2(c1)\n",
    "    yield from callback_3(c2)\n",
    "    return c2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# 然后你再执行以下 caller_use_yield_from(1, 2)，你会发现没有任何输出，直接调用它并没什么用\n",
    "# 因为这个时候有了 yield from语句它成为了协程。\n",
    "# 那我们怎么执行它呢？协程需要调用方来驱动执行，还记得我们之前说的 预激(prime) 吗？\n",
    "\n",
    "c = caller_use_yield_from(1,2)  # coroutine\n",
    "f1 = c.send(None)   # 产出第一个 future 对象\n",
    "f2 = c.send(f1.result)  # 驱动运行到第二个 callback\n",
    "f3 = c.send(f2.result)\n",
    "try:\n",
    "    f4 = c.send(None)\n",
    "except StopIteration as e:\n",
    "    print(e.value)   # 输出结果 6"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# 或者我们还可以用这种方式不断驱动它来执行，（后边我们会看到如何将它演变为 Task 类）:\n",
    "# code_demo/python_async/future_demo.py\n",
    "\n",
    "c = caller_use_yield_from(1, 2)  # coroutine\n",
    "f = Future()\n",
    "f.set_result(None)\n",
    "next_future = c.send(f.result)\n",
    "def step(future):\n",
    "    next_future = c.send(future.result)\n",
    "    next_future.add_done_callback(step)\n",
    "while 1:\n",
    "    try:\n",
    "        step(f)\n",
    "    except StopIteration as e:\n",
    "        print(e.value)   # 输出结果 6\n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "# <center> 协程编程的问题 </center>\n",
    "\n",
    "- 代码看起来更复杂，不过在委派生成器这里，我们用协程和 Future 结合把回调给消除了\n",
    "- 当一个函数的结果依赖另一个函数的时候，我们不需要一个函数回调另一个函数，而是通过协程把上一个协程的结果send(value)发送给下一个依赖它的值的协程\n",
    "- 异步编程更加复杂，但是复杂性被框架掩盖了，业务层代码会大大简化\n",
    "\n",
    "\n",
    "  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "# <center> 用协程和 yield from 改造 TCPEchoServer </center>\n",
    "\n",
    "- 使用 yield from 和 Future 重写\n",
    "- 改写 accept/read/sendall 函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "    def accept(self):\n",
    "        f = Future()\n",
    "\n",
    "        def on_accept():\n",
    "            conn, addr = self.s.accept()\n",
    "            print('accepted', conn, 'from', addr)\n",
    "            conn.setblocking(False)\n",
    "            f.set_result((conn, addr))  # accept 的 result 是接受连接的新对象 conn, addr\n",
    "        self._loop.selector.register(self.s, selectors.EVENT_READ, on_accept)\n",
    "        conn, addr = yield from f  # 委派给 future 对象，直到 future 执行了 socket.accept() 并且把 result 返回\n",
    "        self._loop.selector.unregister(self.s)\n",
    "        return conn, addr\n",
    "    \n",
    "    def read(self, conn):\n",
    "        f = Future()\n",
    "\n",
    "        def on_read():\n",
    "            msg = conn.recv(1024)\n",
    "            f.set_result(msg)\n",
    "        self._loop.selector.register(conn, selectors.EVENT_READ, on_read)\n",
    "        msg = yield from f\n",
    "        return msg\n",
    "\n",
    "    def sendall(self, conn, msg):\n",
    "        f = Future()\n",
    "\n",
    "        def on_write():\n",
    "            conn.sendall(msg)\n",
    "            f.set_result(None)\n",
    "            self._loop.selector.unregister(conn)\n",
    "            conn.close()\n",
    "        self._loop.selector.modify(conn, selectors.EVENT_WRITE, on_write)\n",
    "        yield from f"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# 整个代码如下\n",
    "class TCPEchoServer:\n",
    "    def __init__(self, host, port, loop):\n",
    "        self.host = host\n",
    "        self.port = port\n",
    "        self._loop = loop\n",
    "        self.s = socket.socket()\n",
    "\n",
    "    def run(self):   # 这是我们的应用层代码，看起来比起回调方便很多，但是内部 accept 等函数却做了很多工作\n",
    "        self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n",
    "        self.s.bind((self.host, self.port))\n",
    "        self.s.listen(128)\n",
    "        self.s.setblocking(False)\n",
    "\n",
    "        while True:\n",
    "            conn, addr = yield from self.accept()\n",
    "            msg = yield from self.read(conn)\n",
    "            if msg:\n",
    "                yield from self.sendall(conn, msg)\n",
    "            else:\n",
    "                conn.close()\n",
    "\n",
    "    def accept(self):\n",
    "        f = Future()\n",
    "\n",
    "        def on_accept():\n",
    "            conn, addr = self.s.accept()\n",
    "            print('accepted', conn, 'from', addr)\n",
    "            conn.setblocking(False)\n",
    "            f.set_result((conn, addr))  # accept 的 result 是接受连接的新对象 conn, addr\n",
    "        self._loop.selector.register(self.s, selectors.EVENT_READ, on_accept)\n",
    "        conn, addr = yield from f  # 委派给 future 对象，直到 future 执行了 socket.accept() 并且把 result 返回\n",
    "        self._loop.selector.unregister(self.s)\n",
    "        return conn, addr\n",
    "\n",
    "    def read(self, conn):\n",
    "        f = Future()\n",
    "\n",
    "        def on_read():\n",
    "            msg = conn.recv(1024)\n",
    "            f.set_result(msg)\n",
    "        self._loop.selector.register(conn, selectors.EVENT_READ, on_read)\n",
    "        msg = yield from f\n",
    "        return msg\n",
    "\n",
    "    def sendall(self, conn, msg):\n",
    "        f = Future()\n",
    "\n",
    "        def on_write():\n",
    "            conn.sendall(msg)\n",
    "            f.set_result(None)\n",
    "            self._loop.selector.unregister(conn)\n",
    "            conn.close()\n",
    "        self._loop.selector.modify(conn, selectors.EVENT_WRITE, on_write)\n",
    "        yield from f\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center> Task 驱动协程 </center>\n",
    "\n",
    "- 使用 Future 和 yield from 将函数改造成了生成器协程\n",
    "- 之前说过生成器需要由 send(None) 或者 next 来启动， 之后可以通过 send(value) 的方式发送值并且继续执行。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# 前面我们曾经使用如下方式来运行 caller_use_yield_from 协程:\n",
    "\n",
    "c = caller_use_yield_from(1, 2)  # coroutine\n",
    "f = Future()\n",
    "f.set_result(None)\n",
    "next_future = c.send(f.result)\n",
    "def step(future):\n",
    "    next_future = c.send(future.result)\n",
    "    next_future.add_done_callback(step)\n",
    "while 1:\n",
    "    try:\n",
    "        step(f)\n",
    "    except StopIteration as e:\n",
    "        print(e.value)   # 输出结果 6\n",
    "        break"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# 这里我们重构下这种方式，写一个 Task 对象驱动协程执行。 \n",
    "# 注意我们的 TCPEchoServer.run 方法已经成了协程，我们用 Task 驱动它执行。\n",
    "# 我们创建一个 Task 来管理生成器的执行。\n",
    "\n",
    "class Task:\n",
    "    \"\"\"管理生成器的执行\"\"\"\n",
    "\n",
    "    def __init__(self, coro):\n",
    "        self.coro = coro\n",
    "\n",
    "    def step(self, future):\n",
    "        next_future = self.coro.send(future.result)\n",
    "        next_future.add_done_callback(self.step)\n",
    "\n",
    "    def run(self):\n",
    "        f = Future()\n",
    "        f.set_result(None)\n",
    "        while 1:\n",
    "            try:\n",
    "                self.step(f)\n",
    "            except StopIteration as e:\n",
    "                print(e.value)\n",
    "                return\n",
    "            \n",
    "# 然后我们可以用如下方式使用它：       \n",
    "Task(caller_use_yield_from(1,2)).run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# 不过对于 TCPEchoServer 我们需要事件循环来驱动它，我们把 Task 修改成如下形式：\n",
    "class Task:\n",
    "    \"\"\"管理生成器的执行\"\"\"\n",
    "    def __init__(self, coro):\n",
    "        self.coro = coro\n",
    "        f = Future()\n",
    "        f.set_result(None)\n",
    "        self.step(f)\n",
    "\n",
    "    def step(self, future):\n",
    "        try:  # 把当前 future 的结果发送给协程作为 yield from 表达式的值，同时执行到下一个 future 处\n",
    "            next_future = self.coro.send(future.result)\n",
    "        except StopIteration:\n",
    "            return\n",
    "        next_future.add_done_callback(self.step)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# 然后是我们的 EventLoop 事件循环类：\n",
    "\n",
    "\n",
    "class EventLoop:\n",
    "    def __init__(self, selector=None):\n",
    "        if selector is None:\n",
    "            selector = selectors.DefaultSelector()\n",
    "        self.selector = selector\n",
    "\n",
    "    def create_task(self, coro):\n",
    "        return Task(coro)\n",
    "\n",
    "    def run_forever(self):\n",
    "        while 1:\n",
    "            events = self.selector.select()\n",
    "            for event_key, event_mask in events:\n",
    "                callback = event_key.data\n",
    "                callback()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# 好了，最后我们来启动 TCPEchoServer, \n",
    "\n",
    "event_loop = EventLoop()\n",
    "echo_server = TCPEchoServer('localhost', 8888, event_loop)\n",
    "task = Task(echo_server.run())\n",
    "event_loop.run_forever()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# 所有代码如下：code_demo/python_async/tcp_echo_server_coroutine.py\n",
    "\n",
    "import selectors\n",
    "import socket\n",
    "\n",
    "\n",
    "class Future:\n",
    "    def __init__(self):\n",
    "        self.result = None\n",
    "        self._callbacks = []\n",
    "\n",
    "    def add_done_callback(self, fn):\n",
    "        self._callbacks.append(fn)\n",
    "\n",
    "    def set_result(self, result):\n",
    "        self.result = result\n",
    "        for callback in self._callbacks:\n",
    "            callback(self)\n",
    "\n",
    "    def __iter__(self):\n",
    "        yield self\n",
    "        return self.result\n",
    "\n",
    "\n",
    "class Task:\n",
    "    \"\"\"管理生成器的执行\"\"\"\n",
    "    def __init__(self, coro):\n",
    "        self.coro = coro\n",
    "        f = Future()\n",
    "        f.set_result(None)\n",
    "        self.step(f)\n",
    "\n",
    "    def step(self, future):\n",
    "        try:  # 把当前 future 的结果发送给协程作为 yield from 表达式的值，同时执行到下一个 future 处\n",
    "            next_future = self.coro.send(future.result)\n",
    "        except StopIteration:\n",
    "            return\n",
    "        next_future.add_done_callback(self.step)\n",
    "\n",
    "\n",
    "class TCPEchoServer:\n",
    "    def __init__(self, host, port, loop):\n",
    "        self.host = host\n",
    "        self.port = port\n",
    "        self._loop = loop\n",
    "        self.s = socket.socket()\n",
    "\n",
    "    def run(self):\n",
    "        self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n",
    "        self.s.bind((self.host, self.port))\n",
    "        self.s.listen(128)\n",
    "        self.s.setblocking(False)\n",
    "\n",
    "        while True:\n",
    "            conn, addr = yield from self.accept()\n",
    "            msg = yield from self.read(conn)\n",
    "            if msg:\n",
    "                yield from self.sendall(conn, msg)\n",
    "            else:\n",
    "                conn.close()\n",
    "\n",
    "    def accept(self):\n",
    "        f = Future()\n",
    "\n",
    "        def on_accept():\n",
    "            conn, addr = self.s.accept()\n",
    "            print('accepted', conn, 'from', addr)\n",
    "            conn.setblocking(False)\n",
    "            f.set_result((conn, addr))  # accept 的 result 是接受连接的新对象 conn, addr\n",
    "        self._loop.selector.register(self.s, selectors.EVENT_READ, on_accept)\n",
    "        conn, addr = yield from f  # 委派给 future 对象，直到 future 执行了 socket.accept() 并且把 result 返回\n",
    "        self._loop.selector.unregister(self.s)\n",
    "        return conn, addr\n",
    "\n",
    "    def read(self, conn):\n",
    "        f = Future()\n",
    "\n",
    "        def on_read():\n",
    "            msg = conn.recv(1024)\n",
    "            f.set_result(msg)\n",
    "        self._loop.selector.register(conn, selectors.EVENT_READ, on_read)\n",
    "        msg = yield from f\n",
    "        return msg\n",
    "\n",
    "    def sendall(self, conn, msg):\n",
    "        f = Future()\n",
    "\n",
    "        def on_write():\n",
    "            conn.sendall(msg)\n",
    "            f.set_result(None)\n",
    "            self._loop.selector.unregister(conn)\n",
    "            conn.close()\n",
    "        self._loop.selector.modify(conn, selectors.EVENT_WRITE, on_write)\n",
    "        yield from f\n",
    "\n",
    "\n",
    "class EventLoop:\n",
    "    def __init__(self, selector=None):\n",
    "        if selector is None:\n",
    "            selector = selectors.DefaultSelector()\n",
    "        self.selector = selector\n",
    "\n",
    "    def create_task(self, coro):\n",
    "        return Task(coro)\n",
    "\n",
    "    def run_forever(self):\n",
    "        while 1:\n",
    "            events = self.selector.select()\n",
    "            for event_key, event_mask in events:\n",
    "                callback = event_key.data\n",
    "                callback()\n",
    "\n",
    "\n",
    "event_loop = EventLoop()\n",
    "echo_server = TCPEchoServer('localhost', 8888, event_loop)\n",
    "task = Task(echo_server.run())\n",
    "event_loop.run_forever()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center> 原生协程, async/await </center>\n",
    "\n",
    "- 到目前为止，我们仍然使用的是 基于生成器的协程(generators based coroutines)\n",
    "- python3.5中，python增加了使用async/await语法的原生协程(native coroutines)，使用起来并没有功能上的差别\n",
    "- 我们把之前的所有 yield from 改成 await，同时函数定义前面加上 async 就好了\n",
    "- 注意 Future 需要定义 `__await__` 方法 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# code_demo/python_async/tcp_echo_server_async_await.py\n",
    "class Future:\n",
    "    def __init__(self):\n",
    "        self.result = None\n",
    "        self._callbacks = []\n",
    "\n",
    "    def add_done_callback(self, fn):\n",
    "        self._callbacks.append(fn)\n",
    "\n",
    "    def set_result(self, result):\n",
    "        self.result = result\n",
    "        for callback in self._callbacks:\n",
    "            callback(self)\n",
    "\n",
    "    def __iter__(self):\n",
    "        yield self\n",
    "        return self.result\n",
    "\n",
    "    __await__ = __iter__ # make compatible with 'await' expression\n",
    "\n",
    "class Task:\n",
    "    def __init__(self, coro):\n",
    "        self.coro = coro\n",
    "        f = Future()\n",
    "        f.set_result(None)\n",
    "        self.step(f)\n",
    "\n",
    "    def step(self, future):\n",
    "        try:\n",
    "            next_future = self.coro.send(future.result)\n",
    "        except StopIteration:\n",
    "            return\n",
    "        next_future.add_done_callback(self.step)\n",
    "\n",
    "\n",
    "class TCPEchoServer:\n",
    "    def __init__(self, host, port, loop):\n",
    "        self.host = host\n",
    "        self.port = port\n",
    "        self._loop = loop\n",
    "        self.s = socket.socket()\n",
    "\n",
    "    async def run(self):\n",
    "        self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n",
    "        self.s.bind((self.host, self.port))\n",
    "        self.s.listen(128)\n",
    "        self.s.setblocking(False)\n",
    "\n",
    "        while True:\n",
    "            conn, addr = await self.accept()\n",
    "            msg = await self.read(conn)\n",
    "            if msg:\n",
    "                await self.sendall(conn, msg)\n",
    "            else:\n",
    "                conn.close()\n",
    "\n",
    "    async def accept(self):\n",
    "        f = Future()\n",
    "\n",
    "        def on_accept():\n",
    "            conn, addr = self.s.accept()\n",
    "            print('accepted', conn, 'from', addr)\n",
    "            conn.setblocking(False)\n",
    "            f.set_result((conn, addr))\n",
    "        self._loop.selector.register(self.s, selectors.EVENT_READ, on_accept)\n",
    "        conn, addr = await f\n",
    "        self._loop.selector.unregister(self.s)\n",
    "        return conn, addr\n",
    "\n",
    "    async def read(self, conn):\n",
    "        f = Future()\n",
    "\n",
    "        def on_read():\n",
    "            msg = conn.recv(1024)\n",
    "            f.set_result(msg)\n",
    "        self._loop.selector.register(conn, selectors.EVENT_READ, on_read)\n",
    "        msg = await f\n",
    "        return msg\n",
    "\n",
    "    async def sendall(self, conn, msg):\n",
    "        f = Future()\n",
    "\n",
    "        def on_write():\n",
    "            conn.sendall(msg)\n",
    "            f.set_result(None)\n",
    "            self._loop.selector.unregister(conn)\n",
    "            conn.close()\n",
    "        self._loop.selector.modify(conn, selectors.EVENT_WRITE, on_write)\n",
    "        await f\n",
    "\n",
    "\n",
    "class EventLoop:\n",
    "    def __init__(self, selector=None):\n",
    "        if selector is None:\n",
    "            selector = selectors.DefaultSelector()\n",
    "        self.selector = selector\n",
    "\n",
    "    def create_task(self, coro):\n",
    "        return Task(coro)\n",
    "\n",
    "    def run_forever(self):\n",
    "        while 1:\n",
    "            events = self.selector.select()\n",
    "            for event_key, event_mask in events:\n",
    "                callback = event_key.data\n",
    "                callback()\n",
    "\n",
    "\n",
    "event_loop = EventLoop()\n",
    "echo_server = TCPEchoServer('localhost', 8888, event_loop)\n",
    "task = Task(echo_server.run())\n",
    "event_loop.run_forever()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center> 参考 </center>\n",
    "\n",
    "- 《Fluent Python》\n",
    "\n",
    "- [深入理解 Python 异步编程](https://mp.weixin.qq.com/s/GgamzHPyZuSg45LoJKsofA)\n",
    "\n",
    "- [从 asyncio 简单实现看异步是如何工作的](https://www.4async.com/2016/02/simple-implement-asyncio-to-understand-how-async-works/)\n",
    "\n",
    "- [Python generators, coroutines, native coroutines and async/await]()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center> Thanks </center>\n",
    "\n",
    "\n",
    "<center><img align=\"center\" src=\"imgs/coding.gif\"></center>"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Slideshow",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
